"""J2CL library rules."""

load("@rules_java//java:defs.bzl", "JavaInfo", "JavaPluginInfo")
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load(":j2cl_common.bzl", "J2CL_TOOLCHAIN_ATTRS", "j2cl_common", "split_srcs")
load(":j2cl_js_common.bzl", "J2CL_JS_ATTRS", "JsInfo", "j2cl_js_provider")
load(":j2kt_common.bzl", "j2kt_common")
load(":j2kt_import.bzl", "create_J2ktInfo_for_java_import")
load(":klib_common.bzl", "klib_common")
load(":provider.bzl", "J2clInfo", "J2ktInfo")

def _impl_j2cl_library(ctx):
    enable_j2kt_web = ctx.attr.enable_j2kt_web

    extra_javacopts = [
        # Dagger fast-init is more code size optimal for web, so enable it by default.
        "-Adagger.fastInit=enabled",
        # Disable ThreadSafe checker since we don't support multi-threading and can cause annoyance
        # to users with AutoFactory (http://yaqs/9094400422428278784#a1n2).
        "-Xep:ThreadSafe:OFF",
    ]
    if ctx.attr.optimize_autovalue:
        extra_javacopts.append("-Acom.google.auto.value.OmitIdentifiers")

    if enable_j2kt_web:
        jvm_srcs, js_srcs = split_srcs(ctx.files.srcs)

        # Invoke j2kt transpiler first to transpile Java files to Kotlin.
        j2kt_provider = j2kt_common.compile(
            ctx,
            srcs = jvm_srcs,
            java_deps = [p._private_.java_info for p in _j2kt_providers_of(ctx.attr.deps)],
            j2kt_exports = [p for p in _j2kt_providers_of(ctx.attr.exports)],
            plugins = [p[JavaPluginInfo] for p in ctx.attr.plugins],
            exported_plugins = [p[JavaPluginInfo] for p in ctx.attr.exported_plugins],
            output_jar = ctx.actions.declare_file(ctx.label.name + "_j2kt_web_jvm.jar"),
            javac_opts = extra_javacopts + ctx.attr.javacopts,
            strip_annotations = ["GwtIncompatible", "J2ktIncompatible"],
            # TODO(b/322906767): Remove when the bug is fixed.
            custom_args = [
                "--jvm_flag=-Dcom.google.j2cl.transpiler.backend.kotlin.preserveEqualsForJsTypeInterface=true",
            ],
        )

        # Add any extra JS files that may have been generated by the Java compilation.
        js_srcs.extend(j2kt_provider._private_.extra_js_out)

        # Pass the package-info.java files as srcs so Kotlin frontend can correctly resolved
        # JsPackage annotations.
        # TODO(dramaix): extract package-info from src-jar of the jvm compilation to include
        # generated package-info.java files.
        srcs = js_srcs + [f for f in jvm_srcs if f.basename == "package-info.java"]
        kt_common_srcs = j2kt_provider._private_.transpile_kt_out
    else:
        j2kt_provider = None
        srcs = ctx.files.srcs
        kt_common_srcs = ctx.files.kt_common_srcs

    # Restrict the usage of kotlincopts to internal-only callers.
    # TODO(b/240682589): Setup a way for users to safely pass in flags.
    if (len(ctx.attr.kotlincopts) and not ctx.label.package.startswith("")):
        fail("kotlincopts can only be internally set by J2CL")

    j2cl_provider = j2cl_common.compile(
        ctx,
        srcs = srcs,
        kt_common_srcs = kt_common_srcs,
        deps = _j2cl_or_js_providers_of(ctx.attr.deps),
        exports = _j2cl_or_js_providers_of(ctx.attr.exports),
        plugins = [p[JavaPluginInfo] for p in ctx.attr.plugins],
        exported_plugins = [p[JavaPluginInfo] for p in ctx.attr.exported_plugins],
        javac_opts = extra_javacopts + ctx.attr.javacopts,
        kotlincopts = ctx.attr.kotlincopts,
        internal_transpiler_flags = {
            k: getattr(ctx.attr, k)
            for k in _J2CL_INTERNAL_LIB_ATTRS.keys()
        },
        is_j2kt_web_enabled = enable_j2kt_web,
        klib_friends = klib_common.get_klib_friends(ctx, ctx.attr.deps),
        exported_friend_klibs = klib_common.get_klib_friends(ctx, ctx.attr.exports),
    )

    # If there is no J2ktInfo provider then this target and/or build was not enabled for J2KT.
    # However, we still need to produce a J2ktInfo provider to ensure we keep the J2ktInfo providers
    # in a separate tree from the J2clInfo providers. This prevents them from being mixed together,
    # which can downstream lead to seeing both the pre-J2KT JavaInfo and post-J2KT JavaInfo for a
    # given target.
    if not j2kt_provider:
        # Backstop to make sure we don't silently cover up a missing J2ktInfo
        # provider when the target is actually enabled for J2KT.
        if enable_j2kt_web:
            fail("Target is enabled for J2KT but J2ktInfo was not produced")

        # Create a new JavaInfo provider with all the same compilation jars,
        # but swap the deps/exports to the ones from the J2ktInfo tree.
        java_outputs = j2cl_provider._private_.java_info.java_outputs[0]
        j2kt_java_info = JavaInfo(
            output_jar = java_outputs.class_jar,
            compile_jar = java_outputs.compile_jar,
            header_compilation_jar = java_outputs.header_compilation_jar,
            deps = [p._private_.java_info for p in _j2kt_providers_of(ctx.attr.deps)],
            exports = [p._private_.java_info for p in _j2kt_providers_of(ctx.attr.exports)],
            exported_plugins = [p[JavaPluginInfo] for p in ctx.attr.exported_plugins],
        )

        j2kt_provider = create_J2ktInfo_for_java_import(j2kt_java_info)

    outputs = [
        ctx.outputs.jar,
    ]

    output_js = j2cl_provider._private_.output_js
    if output_js:
        outputs.append(output_js)

    extra_providers = [DefaultInfo(files = depset(outputs))]
    if j2kt_provider:
        extra_providers.append(j2kt_provider)

    # TODO(b/465873783): Remove this when J2KT persists junit annotations.
    if "j2cl_generate_jsunit_suite" in ctx.attr.tags:
        # The jar output of j2kt compile (if enabled) or j2cl compile contains test artifacts
        jar = j2kt_provider._private_.java_info.java_outputs[0].class_jar
        extra_providers.append(OutputGroupInfo(test_artifacts = depset([jar])))

    return j2cl_common.create_js_lib_struct(
        j2cl_info = j2cl_provider,
        extra_providers = extra_providers,
    )

def _j2kt_providers_of(deps):
    return [p for p in [_j2kt_provider_or_none(d) for d in deps] if p != None]

def _j2kt_provider_or_none(dep):
    if J2ktInfo in dep:
        return dep[J2ktInfo]
    if J2clInfo in dep:
        # This should really only be the case for j2cl_import'd targets.
        return create_J2ktInfo_for_java_import(dep[J2clInfo]._private_.java_info)

    # This is a JS dep. This is not needed in the context of J2KT.
    return None

def _j2cl_or_js_providers_of(deps):
    return [_j2cl_or_js_provider_of(d) for d in deps]

def _j2cl_or_js_provider_of(dep):
    return dep[J2clInfo] if J2clInfo in dep else dep[JsInfo]

_J2KT_WEB_EXPERIMENT_ATTRS = {
    "enable_j2kt_web": attr.bool(
        doc = "Whether J2KT Web is enabled for this specific target.",
        default = False,
    ),
}

_J2CL_INTERNAL_LIB_ATTRS = {
    "readable_source_maps": attr.bool(default = False),
    "readable_library_info": attr.bool(default = False),
    "optimize_autovalue": attr.bool(default = True),
    "experimental_enable_jspecify_support_do_not_enable_without_jspecify_static_checking_or_you_might_cause_an_outage": attr.bool(default = False),
}

_J2CL_LIB_ATTRS = {
    # TODO(goktug): Try to limit this further.
    "srcs": attr.label_list(allow_files = [".java", ".kt", ".js", ".srcjar", ".jar", ".zip"]),
    "kt_common_srcs": attr.label_list(allow_files = [".kt"]),
    "deps": attr.label_list(providers = [JsInfo]),
    "exports": attr.label_list(providers = [JsInfo]),
    "plugins": attr.label_list(allow_rules = ["java_plugin", "java_library"], cfg = "exec"),
    "exported_plugins": attr.label_list(allow_rules = ["java_plugin", "java_library"], cfg = "exec"),
    "javacopts": attr.string_list(),
    "kotlincopts": attr.string_list(),
}

_J2CL_LIB_ATTRS.update(_J2KT_WEB_EXPERIMENT_ATTRS)
_J2CL_LIB_ATTRS.update(_J2CL_INTERNAL_LIB_ATTRS)
_J2CL_LIB_ATTRS.update(J2CL_TOOLCHAIN_ATTRS)
_J2CL_LIB_ATTRS.update(J2CL_JS_ATTRS)

j2cl_library = rule(
    implementation = _impl_j2cl_library,
    attrs = _J2CL_LIB_ATTRS,
    fragments = ["java", "js"],
    toolchains = ["@bazel_tools//tools/jdk:toolchain_type"],
    outputs = {
        "jar": "lib%{name}.jar",
        "srcjar": "lib%{name}-src.jar",
    },
)

def _impl_java_import(ctx):
    java_info = ctx.attr.jar[JavaInfo]
    return j2cl_common.create_js_lib_struct(
        J2clInfo(
            _private_ = struct(
                java_info = java_info,
                js_info = j2cl_js_provider(ctx),
                library_info = [],
                klib_info = klib_common.create_klib_info_for_import(java_info),
            ),
            _is_j2cl_provider = 1,
        ),
    )

_J2CL_IMPORT_ATTRS = {
    "jar": attr.label(providers = [JavaInfo]),
}
_J2CL_IMPORT_ATTRS.update(J2CL_TOOLCHAIN_ATTRS)

# helper rule to convert a Java target to a J2CL target.
j2cl_java_import = rule(
    implementation = _impl_java_import,
    attrs = _J2CL_IMPORT_ATTRS,
    fragments = ["java", "js"],
)
